#pragma once
#include <signal.h>
#include <sys/resource.h>

#include "../comm/Util.hpp"
#include "../comm/Log.hpp"

namespace ns_runner
{
    using namespace ns_util;
    using namespace ns_log;

    class runner
    {
    private:
    public:
        /**********
         * cpu_limit:cpu最大运行时间。
         * mem_limit:内存最大使用限制。
         **********/
        static void SetProLimit(int cpu_limit, int mem_limit)
        {
            struct rlimit cpu_rlimit;
            cpu_rlimit.rlim_max = RLIM_INFINITY;
            cpu_rlimit.rlim_cur = cpu_limit;
            setrlimit(RLIMIT_CPU, &cpu_rlimit);

            struct rlimit mem_rlimit;
            mem_rlimit.rlim_max = RLIM_INFINITY;
            mem_rlimit.rlim_cur = mem_limit * 1024;
            setrlimit(RLIMIT_AS, &mem_rlimit);
        }
        /*******************************************************************
         * 传入带路径文件名file_name,以此名创建各种后缀文件。
         * cpu_limit:设置程序最大运行时间。
         * mem_limit:设置内存最大占用值。
         * 返回值: 1.大于0:信号终止，值为对应信号。
         *         2.小于0:异常终止。
         *         3.等于0:正常运行结束。
         * *****************************************************************/
        static int Run(std::string &file_name, int cup_limit, int mem_limit)
        {
            //  * 1.创建Stdin临时文件存放用户输入参数。(重定向标准输入)
            //  * 2.创建Stdout临时文件存放用程序输出内容。(重定向标准输出)
            //  * 3.创建Stderr临时文件存放用程序出错内容。(重定向标准错误)
            //  * 执行file_name文件

            //  一定有file_name可执行文件。
            struct stat buff;
            stat(file_name.c_str(), &buff);

            std::string Stdin_file = PathUtil::AddSuffixStdin(file_name);
            std::string Stdout_file = PathUtil::AddSuffixStdout(file_name);
            std::string Stderr_file = PathUtil::AddSuffixStderr(file_name);

            umask(0);
            int fd_in = open(Stdin_file.c_str(), O_CREAT | O_WRONLY, 0755);
            int fd_out = open(Stdout_file.c_str(), O_CREAT | O_WRONLY, 0755);
            int fd_err = open(Stderr_file.c_str(), O_CREAT | O_WRONLY, 0755);

            int pid = fork();
            if (pid < 0)
            {
                // 子进程创建失败
                LOG(ERROR) << "运行子进程创建失败" << "\n";
                close(fd_in);
                close(fd_out);
                close(fd_err);
                return -2;
            }
            // 子进程
            else if (pid == 0)
            {
                // 设置cpu与内存限制
                SetProLimit(cup_limit, mem_limit);
                dup2(fd_in, 0);
                dup2(fd_out, 1);
                dup2(fd_err, 2);
                if (execl(file_name.c_str(), file_name.c_str(), nullptr) < 0)
                {
                    // 程序替换错误
                    LOG(ERROR) << "运行子进程程序替换失败" << "\n";
                }
                exit(1);
            }
            // 父进程
            else
            {
                close(fd_in);
                close(fd_out);
                close(fd_err);
                int status;
                if (waitpid(pid, &status, 0) < 0)
                {
                    // 等待错误
                    LOG(ERROR) << "waitpid error" << "\n";
                    return -3;
                }

                if ((status & 0x7F))
                {
                    // 返回退出的信号
                    int sigNum = (status & 0x7F);
                    // LOG(DEBUG) << "子进程运行终止,收到信号: " << sigNum << "\n";
                    return sigNum;
                }
                else if (((status >> 8) & 0xFF))
                {
                    // LOG(DEBUG) << "子进程错误运行，退出码： " << ((status >> 8) & 0xFF) << "\n";
                    return -4;
                }
            }
            // LOG(DEBUG) << "子进程运行完成" << "\n";
            return 0;
        }
    };
}